package pl.agh.convexhulltutor.algorithms;

import java.awt.Color;
import java.util.Collections;
import java.util.List;
import java.util.Stack;

import pl.agh.convexhulltutor.gui.DrawPanel;
import pl.agh.convexhulltutor.structures.Line;
import pl.agh.convexhulltutor.structures.Point;

public class UpperAndLowerConvexHullAlgorithm extends Algorithm {
	private Stack<Point> stack = new Stack<Point>();
	private int verticesIndex = 2;
	private boolean stepInProgress = false;
	private boolean upperConvexHull = true;

	public UpperAndLowerConvexHullAlgorithm(DrawPanel drawPanel) {
		super(drawPanel);
	}

	private Color getConvexHullLineColor() {
		return upperConvexHull ? Line.UPPER_CONVEX_HULL_COLOR
				: Line.LOWER_CONVEX_HULL_COLOR;
	}

	@Override
	public void execute(List<Point> points, List<Line> lines) {
		if (currentStep == 1
				|| (verticesIndex == points.size() && stepInProgress == false)) {
			Point point1;
			Point point2;
			if (currentStep == 1) {
				Collections.sort(points);
				point1 = points.get(0);
				point2 = points.get(1);
			} else {
				upperConvexHull = false;
				verticesIndex -= 3;
				point1 = points.get(points.size() - 1);
				point2 = points.get(points.size() - 2);
			}
			point1.setColor(Point.CONVEX_HULL_COLOR);
			point2.setColor(Point.CONVEX_HULL_COLOR);
			lines.add(new Line(point1, point2, getConvexHullLineColor()));
			stack.push(point1);
			stack.push(point2);
		} else {
			if (stepInProgress == false) {
				if (verticesIndex == 0 && upperConvexHull == false) {
					maxStep = currentStep + 1;
				}
				if (verticesIndex >= 0) {
					Point currentPoint = points.get(verticesIndex);
					if (upperConvexHull) {
						verticesIndex++;
					} else {
						verticesIndex--;
					}
					currentPoint.setColor(Point.CONVEX_HULL_COLOR);
					lines.add(new Line(stack.peek(), currentPoint,
							getConvexHullLineColor()));
					stack.push(currentPoint);
					stepInProgress = true;
				}
			} else {
				if (stack.size() >= 3) {
					Point previousPointPlusOne = stack.get(stack.size() - 3);
					Point previousPoint = stack.get(stack.size() - 2);
					Point currentPoint = stack.get(stack.size() - 1);
					if (getDeterminantSign(previousPoint, previousPointPlusOne,
							currentPoint) > 0) {
						Point updatedPreviousPointPlusOne = getUpdatedPoint(
								previousPointPlusOne, points);
						Point updatedPreviousPoint = getUpdatedPoint(
								previousPoint, points);
						Point updatedCurrentPoint = getUpdatedPoint(
								currentPoint, points);
						updatedPreviousPoint.setPreviousColor();
						lines.remove(lines.size() - 1);
						lines.remove(lines.size() - 1);
						lines.add(new Line(updatedCurrentPoint,
								updatedPreviousPointPlusOne,
								getConvexHullLineColor()));
						stack.remove(previousPoint);
					} else {
						stepInProgress = false;
					}
				} else {
					stepInProgress = false;
				}
			}
		}
		if (currentStep == maxStep) {
			for (Point point : points) {
				if (point.getColor() != Point.CONVEX_HULL_COLOR) {
					point.setColor(Point.NON_CONVEX_HULL_COLOR);
				}
			}
			for (Line line : lines) {
				line.setColor(Line.CONVEX_HULL_COLOR);
			}
		}
	}
}
